#pragma once

#include <iostream>
#include <string>
#include <cerrno>
#include <cstring>
#include <cstdlib>
#include <strings.h>
#include "Log.hpp"
#include "InetAddr.hpp"
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <functional>

enum
{
    SOCKET_ERROR = 1,
    BIND_ERROR,
    USAGE_ERROR
};

const static int defaultfd = -1;

using hander_message_t = std::function<void(int sokfd,const InetAddr who,const std::string message)>;

class UdpServer
{
public:
    UdpServer(uint16_t port,hander_message_t hander_message)
        : _socketfd(defaultfd),
          _port(port),
          _isrunning(false),
          _hander_message(hander_message)
    {
    }

    void InitSerrver()
    {
        // 1 创建套接字
        // 成功返回文件描述符
        _socketfd = socket(AF_INET, SOCK_DGRAM, 0);

        if (_socketfd < 0)
        {
            LOG(ERROR, "socket error:%s,%d\n", strerror(errno), errno);
            exit(SOCKET_ERROR);
        }

        LOG(INFO, "socketfd:%d\n", _socketfd);

        // 2 填充sockaddr_in结构体
        // local再用户栈上开辟空间
        struct sockaddr_in local;
        // 将 local 变量的内存区域清零
        bzero(&local, sizeof(local));
        local.sin_family = AF_INET;
        // port是主机字节序，需要转换为网络字节序(大端)
        local.sin_port = htons(_port);
        // 监听所有可用网络接口
        local.sin_addr.s_addr = htonl(INADDR_ANY);

        // 3 bind socketfd和网络信息
        //套接字绑定到指定的本地地址和端口
        int n = bind(_socketfd, (struct sockaddr *)&local, sizeof(local));

        if (n < 0)
        {
            LOG(ERROR, "bind error:%s,%d\n", strerror(errno), errno);
            exit(BIND_ERROR);
        }
        LOG(INFO, "bind success\n");
    }


    void Start()
    {
        //一直运行，直到管理员自己停止
        //udp是面向数据报的协议

        _isrunning = true;

        while(true)
        {
            char buffer[1024];

            struct sockaddr_in peer;
            //必须初始化为sizeof(peer)
            socklen_t len = sizeof(peer);

            ssize_t n=recvfrom(_socketfd, buffer, sizeof(buffer)-1, 0, (struct sockaddr *)&peer, &len);
            
            if(n>0)
            {
                buffer[n]=0;
                InetAddr addr(peer);
               
               LOG(DEBUG,"get message from [%s:%d]:%s\n",addr.GetIp().c_str(),addr.GetPort(),buffer);

                //回复客户端
               // sendto(_socketfd, buffer, strlen(buffer), 0, (struct sockaddr *)&peer, len);

                _hander_message(_socketfd,addr,buffer);
            }

        }
         
         _isrunning = false;
    }
private:
    int _socketfd;
    uint16_t _port;
    bool _isrunning;
    
     // 给服务器设定回调，用来让上层进行注册业务的处理方法
     hander_message_t _hander_message;
};